package labilabyrinth.game;

import java.util.ArrayList;

import labilabyrinth.enums.BallState;
import labilabyrinth.enums.GameState;
import labilabyrinth.game.R;
import labilabyrinth.serializable.FinishHole;
import labilabyrinth.serializable.Hole;

import android.graphics.*;
import android.util.Log;

/**
 * Trieda pre zobrazenie gulicky na ploche
 * gulicka si vyratava smer a rychlost pohybe na zaklade udajov z accelerometra
 * 
 * @author Lukas Dilik
 */
public class Ball{
	
	//paremetre gulicky
	private Bitmap ballBitmap;
	private BallState state;
	private float size;
	private float radius;
	private PointF center;
	private PointF velocityVector;
	private final int interval = 15; 
	private final int circumferencePointsCount = 360; 
	private final float invElasticity = 4.0f; //cim mensie cislo tym sa odraza
	//informacie o mape kde sa pohybuje gulicka
	private GameSurface gameSurface;
	private ArrayList<RectF> obstacles;
	private ArrayList<Hole> holes;
	private ArrayList<PointF> circumferencePoints;
	private Hole finalPoint = null;
	private final float MAX_SPEED = 10.0f; 
	private RectF touchedRect =  new RectF();

	public BallState getState() {
		return state;
	}

	public PointF getVelocityVector() {
		return velocityVector;
	}

	/**
	 * nastavi gulicku na novu poziciu
	 * potrebne pri pohybe
	 * @param x nove X
	 * @param y nove Y
	 */
	public void setPosition(float x, float y){
		center.x = x;
		center.y = y;
	}
	
	/**
	 * vyrata lavy horny roh gulicky pre vykreslenie bitmapy
	 */
	private PointF calculateBitmapPoint(){
		PointF temp =  new PointF();
		temp.x = (center.x-radius);
		temp.y = (center.y-radius);
		return temp;
	}
	
	/**
	 * vyrata diskretne body po obvode gulicky 
	 */
	private ArrayList<PointF> calculateCircumPoints(){
		int u=-45;
		circumferencePoints = new ArrayList<PointF>(circumferencePointsCount);
		
		for(int i=0;i<circumferencePointsCount/interval;i++){
			float fi = (float) Math.toRadians(u);
			PointF p = new PointF();
			p.x = (float) Math.round( Math.cos(fi)*radius );
			p.y = (float) Math.round( Math.sin(fi)*radius );
			Log.d("surface",Integer.toString(i)+". "+ Integer.toString(u)+"st "+Float.toString(p.x)+" : "+Float.toString(p.y));
			circumferencePoints.add(p);
			u+=interval; 
		}
		return circumferencePoints;
		
	}
	
	/**
	 * preskaluje bitmapu na zadanu velkost
	 * @return nova preskalovana Bitmapa
	 */
	private Bitmap scaleBitmap(Bitmap bmp, int newWidth, int newHeight){
		return Bitmap.createScaledBitmap(bmp, newWidth, newHeight, true);
	}
	
	/**
	 * inicializuje gulicku
	 * nastavi bitmapu pre zobrazenie gulicky
	 * @param size - velkost gulicky
	 */
	public Ball(float size,GameSurface gs){
		this.gameSurface = gs;
		this.obstacles = gs.getObstacles();
		this.holes = gs.getCurrentLevel().getHoles();
		this.size = size;
		this.radius = size /2;
		this.center =  new PointF(0,0);
		this.velocityVector = new PointF(0,0);
		this.state = BallState.LIVING;
		calculateCircumPoints();
		Bitmap tempBall =  BitmapFactory.decodeResource(gs.getContext().getResources(), R.drawable.orange_ball);
		this.ballBitmap = this.scaleBitmap(tempBall, (int)this.size, (int)this.size);
		touchedRect = new RectF();
	}
	
	/**
	 * podla udajov zmeni vektor rychlosti a vektoru smeru pohybu gulicky a pohne ju tym smerom
	 * @param ax - zrychlenie v m/s x osi
	 * @param ay - zrychlenie v m/s y osi
	 * @param dT - zmena casu
	 */
	public void updatePhysics(float ax, float ay, float dT){
		if(state.equals(BallState.LIVING)){
			dT  = gameSurface.round(dT, 5);
			if(Math.abs(velocityVector.x) <= MAX_SPEED){
				velocityVector.x += ax*dT;
				velocityVector.x = gameSurface.round(velocityVector.x, 5);
			}
			if(Math.abs(velocityVector.y) <= MAX_SPEED){
				velocityVector.y += ay*dT;
				velocityVector.y = gameSurface.round(velocityVector.y, 5);
			}
			resolveCollision();
			move();
		}else{
			moveIntoHole();
			checkState();
		}
	}
	
	private void checkForHoles(PointF center){
		Hole res;
		for(Hole hh:this.holes){
			res = hh.isInHole(center);
			if(res != null){
				finalPoint = res;
				state = BallState.DYING;
			}
		}
	}
	
	/**
	 * riesi kolizie gulicky zo stenami
	 * posuniem sa ako keby s gulickou dopradu o mVector
	 * max o sirku steny aby som neprestrelil stenu :D
	 * skontrolujem ci nie som v nejakom obstacle
	 * kontaktny bod ak som tak si ulozim a podla jeho suradnic a stredu 
	 * zistim z ktorej svet. strany som narazil a podla sa zariadim
	 */
	private void resolveCollision(){
		PointF newCenter =  new PointF(center.x,center.y);
	    newCenter.x += velocityVector.x ;
		newCenter.y += velocityVector.y ;
		
		//kontrolujem prekazky a jamy
		int indexOfTouchPoint = -1;
		checkForHoles(center);
		for(RectF r:this.obstacles){
			for(int i =0; i< circumferencePoints.size();i++){
				if(r.contains((circumferencePoints.get(i).x+center.x),(circumferencePoints.get(i).y+center.y))){
					indexOfTouchPoint = i;
					touchedRect = r;
				}
					
			}
		}
		
		if(indexOfTouchPoint >= 0){	
		    if(indexOfTouchPoint < 6){
				rightCollision();
				return;
		    }
		    if(indexOfTouchPoint < 12){
				topCollision();
				return;
		    }
		    if(indexOfTouchPoint < 18){
				leftCollision();
				return;
		    }
		    if(indexOfTouchPoint < 24){
		    	bottomCollision();
				return;
		    }
		}
	}
	
	private void topCollision(){
		center.y = (touchedRect.top)-(radius);
		velocityVector.y *= -(1.0f/invElasticity);
	}
	
	private void rightCollision(){
		center.x = (touchedRect.left)-(radius);
		velocityVector.x *= -(1.0f/invElasticity);
	}
	
	private void bottomCollision(){
		center.y = (touchedRect.bottom)+(radius);
		velocityVector.y *= -(1.0f/invElasticity);
	}
	
	private void leftCollision(){
		center.x = (touchedRect.right)+(radius);
		velocityVector.x *= -(1.0f/invElasticity);
	}
	
	/**
	 * pohne gulicku o dany vektor zrychlenia
	 */
	private void move(){
		center.x += velocityVector.x;
		center.y += velocityVector.y;
	}
	
	public void checkState(){
		if(finalPoint != null){
			if(finalPoint instanceof FinishHole)
				gameSurface.state = GameState.LEVELCOMPLETED;
			else {
				gameSurface.state = GameState.LEVELFAILED;;
			}
		}
		
	}
	
	/**
	 * pohne gulicku do danej diery rychlostou 1px/s
	 */
	private void moveIntoHole(){
		center.x = finalPoint.getCenter().x+2;
		center.y = finalPoint.getCenter().y+2;
		checkState();
	}
	
	
	/**nakresli gulicky na plochu
	 * @param canvas - canvas surface view kde sa kresli
	 */
	public void draw(Canvas canvas){
		 PointF p = calculateBitmapPoint();
		 canvas.drawBitmap(ballBitmap, p.x, p.y, null);
	     /*// DEBUG
		 Paint paint = new Paint();
	     paint.setColor(Color.WHITE);
	     paint.setTextSize(25);
	     canvas.drawRect(touchedRect, paint);
	     paint.setColor(Color.RED);
	    // canvas.drawCircle(center.x, center.y, 1, paint);
	     //for(PointF pp:circumferencePoints){
	    	// canvas.drawCircle(pp.x+center.x, pp.y+center.y, 1, paint);
	     //}*/
	}
	
	public String toString(){
		return size+"|"+radius+"|["+center.x+","+center.y+"]";
	}

}
